3.7.ACL权限管理(zero-rbac)

白皮书中的权限管理主要包括两部分:管理端和消费端

  • 管理端:可扩展面板式架构,现阶段版本支持 菜单管理、流程定制 两类,您可以扩展自己所需的不同权限管理的结构。

  • 消费端:XBAC模型:基于角色的RBAC(Role)、基于属性的ABAC(Attribute)、基于策略的PBAC(Policy)。

Zero在线教程中讲解过可插拔模式的认证组件,此处略过认证部分(@Wall ),本书主要讲解扩展模块中的授权以及 zero-rbac 模块如何实现企业级需求的复杂授权流程,其中标准化认证采用了 OAuth 规范。

3.7.1.核心概念

3.7.1.1.XBAC模型

Zero中的基本权限主体模型如下:

zarch perm

上图中的核心概念如下:

实体表 概念 含义

S_USER

用户

登录系统专用账号信息表。

S_ROLE

角色

和账号关联的角色信息表。

S_GROUP

用户组

和账号关联的用户组信息表。

S_PERMISSION

权限

和角色相关联的权限记录表。

S_PERM_SET

权限集

服务于权限定义的权限集合表,主要用于管理段构造权限集合实现批量授权。

S_ACTION

操作

隶属于权限记录的操作集合,操作最终会绑定到对应的操作主体上。

  • 所有关联表(R_ 前缀)目前都是多对多结构,一个用户可关联多个角色,一个用户可关联多个用户组,一个用户组关联多个角色,一个角色关联多个权限记录,一个权限记录包含多个 安全操作

  • 用户组支持继承结构,包括父组和子组的概念,权限计算过程中,不同算法在父子级结构中发挥的作用会有所区别。

  • 角色和用户组关联过程中支持优先级,即用户在关联角色和用户组时存在 第一角色、第一用户组 的概念。

常用的基本配置中,只要资源所需操作级别和基础算法模型计算的最终权限路径和用户所拥有的权限路径是通的,那么就可以实现基础权限的认证,这部分内容在完成本章节所有讲解之后会有更深入的说明。

安全操作 S_ACTION 对端关联到资源表 S_RESOURCE 和资源形成1对1的绑定,资源中定义的就是访问资源的基本要求,而S_ACTION中计算出来的结果就是登录账号所拥有的资源访问资格,当资源访问资格满足了资源基本要求时就认证通过,证明当前账号有权限访问该资源。但是、但是、但是——此处只是解决了账号:能不能 的问题,在资源访问之后还会有两张专用的数据表来执行 访问多少 的问题,这是Zero中数据域的实现原理,在后续的 N维安全视图 中加以说明。

3.7.1.2.多态身份(Profile)

Zero中由于复杂的多对多结构,最终会形成不同的多态身份(Profile),S_RESOURCE 表中有如下字段对资源访问资格执行定义:

字段名 含义 取值

MODE_ROLE

按角色查找资源的模式

UNION、EAGER、LAZY、INTERSECT

MODE_GROUP

按用户组查找资源的模式

HORIZON、CRITICAL、OVERLOOK

MODE_TREE

用户组继承和非继承树模式查找

EXTEND、PARENT、CHILD、INHERIT

上述取值是资源对多态身份的定义,最终形成的多态身份 Profile 的值列表如下(举例定义角色和组的优先级):

  • 多态身份只有在多角色、关联用户组、多用户组模式下生效,如果只包含一个角色,多态身份会演变成最简单的 USER_UNION 模式。

  • 用户/角色、用户组/角色在计算时都支持 UNION, EAGER, LAZY, INTERSECT 四种模式。

  • 使用用户组模式时,只要计算出选择使用什么用户组,就可以执行用户组对应的角色关联权限集的计算。

不含组模式

假设用户包含如下信息,这种模式(高频使用模式)下 MODE_GROUP / MODE_TREE 都设置成 NULL

R1(H):P1、P2、P3,高优先级角色,包含三条权限记录。
R2(L):P2、P4,低优先级角色,包含两条权限记录。

zero p user

含义

USER_UNION

zero p user union 并集模式,最终权限集为 P1, P2, P3, P4

USER_EAGER

zero p user eager 高优先级模式,使用最高优先级角色的权限集,此处:P1, P2, P3

USER_LAZY

zero p user lazy 低优先级模式,使用最低优先级角色的权限集,此处:P2, P4

USER_INTERSECT

zero p user intersect 交集模式,最终权限集为 P2

用户组模式

假设用户包含如下信息:

用户组结构如下(此处不列举组所对应的权限集):
         G10
        /   \
      G20   G21
     /   \    \
    G30  G31  G32
而登录用户只包含如下三个用户组:
G20(H):高优先级用户组
G31(M):中优先级用户组
G32(L):低优先级用户组
全量优先级:
G10 > G20 > G21 > G30 > G31 > G32

zero p group

组计算 角色计算 计算流程

HORIZON_UNION

zero p group u

zero p user union

UNION模式计算单个用户组关联角色权限集,再将三个组的权限集合并。

HORIZON_EAGER

zero p group u

zero p user eager

EAGER模式计算单个用户组关联角色权限集,再将三个组的权限集合并。

HORIZON_LAZY

zero p group u

zero p user lazy

LAZY模式计算单个用户组关联角色权限集,再将三个组的权限集合并。

HORIZON_INTERSECT

zero p group u

zero p user intersect

INTERSECT模式计算单个用户组关联角色权限集,再将三个组的权限集合并。

CRITICAL_UNION

zero p group h

zero p user union

UNION模式计算高优先级组的权限集。

CRITICAL_EAGER

zero p group h

zero p user eager

EAGER模式计算高优先级组的权限集。

CRITICAL_LAZY

zero p group h

zero p user lazy

LAZY模式计算高优先级组的权限集。

CRITICAL_INTERSECT

zero p group h

zero p user intersect

INTERSECT模式计算高优先级的权限集。

OVERLOOK_UNION

zero p group l

zero p user union

UNION模式计算低优先级组的权限集。

OVERLOOK_EAGER

zero p group l

zero p user eager

EAGER模式计算低优先级组的权限集。

OVERLOOK_LAZY

zero p group l

zero p user lazy

LAZY模式计算低优先级组的权限集。

OVERLOOK_INTERSECT

zero p group l

zero p user intersect

INTERSECT模式计算低优先级组的权限集。

PARENT_HORIZON_UNION

zero p parent gu

zero p user union

先查找三个组的父组,再按UNION模式计算权限集。

PARENT_HORIZON_EAGER

zero p parent gu

zero p user eager

先查找三个组的父组,再按EAGER模式计算权限集。

PARENT_HORIZON_LAZY

zero p parent gu

zero p user lazy

先查找三个组的父组,再按LAZY模式计算权限集。

PARENT_HORIZON_INTERSECT

zero p parent gu

zero p user intersect

先查找三个组的父组,再按INTERSECT模式计算权限集。

PARENT_CRITICAL_UNION

zero p parent gh

zero p user union

查找优先级最高组的父组,再按UNION模式计算权限集。

PARENT_CRITICAL_EAGER

zero p parent gh

zero p user eager

查找优先级最高组的父组,再按EAGER模式计算权限集。

PARENT_CRITICAL_LAZY

zero p parent gh

zero p user lazy

查找优先级最高组的父组,再按LAZY模式计算权限集。

PARENT_CRITICAL_INTERSECT

zero p parent gh

zero p user intersect

查找优先级最高组的父组,再按INTERSECT模式计算权限集。

PARENT_OVERLOOK_UNION

zero p parent gl

zero p user union

查找优先级最低组的父组,再按UNION模式计算权限集。

PARENT_OVERLOOK_EAGER

zero p parent gl

zero p user eager

查找优先级最低组的父组,再按EAGER模式计算权限集。

PARENT_OVERLOOK_LAZY

zero p parent gl

zero p user lazy

查找优先级最低组的父组,再按LAZY模式计算权限集。

PARENT_OVERLOOK_INTERSECT

zero p parent gl

zero p user intersect

查找优先级最低组的父组,再按INTERSECT模式计算权限集。

CHILD_HORIZON_UNION

zero p child gu

zero p user union

查找所有组的子组,再按UNION模式计算权限集。

CHILD_HORIZON_EAGER

zero p child gu

zero p user eager

查找所有组的子组,再按EAGER模式计算权限集。

CHILD_HORIZON_LAZY

zero p child gu

zero p user lazy

查找所有组的子组,再按LAZY模式计算权限集。

CHILD_HORIZON_INTERSECT

zero p child gu

zero p user intersect

查找所有组的子组,再按INTERSECT模式计算权限集。

CHILD_CRITICAL_UNION

zero p child gu

zero p user union

查找优先级最高组的子组,再按UNION模式计算权限集。

CHILD_CRITICAL_EAGER

zero p child gu

zero p user eager

查找优先级最高组的子组,再按EAGER模式计算权限集。

CHILD_CRITICAL_LAZY

zero p child gu

zero p user lazy

查找优先级最高组的子组,再按LAZY模式计算权限集。

CHILD_CRITICAL_INTERSECT

zero p child gu

zero p user intersect

查找优先级最高组的子组,再按INTERSECT模式计算权限集。

CHILD_OVERLOOK_UNION

zero p child gl

zero p user union

(无权限)查找优先级最低组的子组,再按UNION模式计算权限集。

CHILD_OVERLOOK_EAGER

zero p child gl

zero p user eager

(无权限)查找优先级最低组的子组,再按EAGER模式计算权限集。

CHILD_OVERLOOK_LAZY

zero p child gl

zero p user lazy

(无权限)查找优先级最低组的子组,再按LAZY模式计算权限集。

CHILD_OVERLOOK_INTERSECT

zero p child gl

zero p user intersect

(无权限)查找优先级最低组的子组,再按INTERSECT模式计算权限集。

INHERIT_HORIZON_UNION

zero p inherit gu

zero p user union

查找所有组父组包含本组,再按UNION模式计算权限集。

INHERIT_HORIZON_EAGER

zero p inherit gu

zero p user eager

查找所有组父组包含本组,再按EAGER模式计算权限集。

INHERIT_HORIZON_LAZY

zero p inherit gu

zero p user lazy

查找所有组父组包含本组,再按LAZY模式计算权限集。

INHERIT_HORIZON_INTERSECT

zero p inherit gu

zero p user intersect

查找所有组父组包含本组,再按INTERSECT模式计算权限集。

INHERIT_CRITICAL_UNION

zero p inherit gh

zero p user union

查找优先级高组的父组包含本组,再按UNION模式计算权限集。

INHERIT_CRITICAL_EAGER

zero p inherit gh

zero p user eager

查找优先级高组的父组包含本组,再按EAGER模式计算权限集。

INHERIT_CRITICAL_LAZY

zero p inherit gh

zero p user lazy

查找优先级高组的父组包含本组,再按LAZY模式计算权限集。

INHERIT_CRITICAL_INTERSECT

zero p inherit gh

zero p user intersect

查找优先级高组的父组包含本组,再按INTERSECT模式计算权限集。

INHERIT_OVERLOOK_UNION

zero p inherit gl

zero p user union

查找优先级低组的父组包含本组,再按UNION模式计算权限集。

INHERIT_OVERLOOK_EAGER

zero p inherit gl

zero p user eager

查找优先级低组的父组包含本组,再按EAGER模式计算权限集。

INHERIT_OVERLOOK_LAZY

zero p inherit gl

zero p user lazy

查找优先级低组的父组包含本组,再按LAZY模式计算权限集。

INHERIT_OVERLOOK_INTERSECT

zero p inherit gl

zero p user intersect

查找优先级低组的父组包含本组,再按INTERSECT模式计算权限集。

EXTEND_HORIZON_UNION

zero p extend gu

zero p user union

查找所有组子组包含本组,再按UNION模式计算权限集。

EXTEND_HORIZON_EAGER

zero p extend gu

zero p user eager

查找所有组子组包含本组,再按EAGER模式计算权限集。

EXTEND_HORIZON_LAZY

zero p extend gu

zero p user lazy

查找所有组子组包含本组,再按LAZY模式计算权限集。

EXTEND_HORIZON_INTERSECT

zero p extend gu

zero p user intersect

查找所有组子组包含本组,再按INTERSECT模式计算权限集。

EXTEND_CRITICAL_UNION

zero p extend gh

zero p user union

查找优先级高组的子组包含本组,再按UNION模式计算权限集。

EXTEND_CRITICAL_EAGER

zero p extend gh

zero p user eager

查找优先级高组的子组包含本组,再按EAGER模式计算权限集。

EXTEND_CRITICAL_LAZY

zero p extend gh

zero p user lazy

查找优先级高组的子组包含本组,再按LAZY模式计算权限集。

EXTEND_CRITICAL_INTERSECT

zero p extend gh

zero p user intersect

查找优先级高组的子组包含本组,再按INTERSECT模式计算权限集。

EXTEND_OVERLOOK_UNION

zero p extend gl

zero p user union

查找优先级低组的子组包含本组,再按UNION模式计算权限集。

EXTEND_OVERLOOK_EAGER

zero p extend gl

zero p user eager

查找优先级低组的子组包含本组,再按EAGER模式计算权限集。

EXTEND_OVERLOOK_LAZY

zero p extend gl

zero p user lazy

查找优先级低组的子组包含本组,再按LAZY模式计算权限集。

EXTEND_OVERLOOK_INTERSECT

zero p extend gl

zero p user intersect

查找优先级低组的子组包含本组,再按INTERSECT模式计算权限集。

多态身份Profile是整个 Zero权限框架中的一个 过度设计 的典范,从实际场景看起来真正使用到这部分的内容仅局限于 USER_X 四种计算模式。但根据设计时的调研和考察,有这部分功能之后,对于复杂组织架构之间的权限控制可以达到非常细粒度的级别,并且在变化过程中,可实现更多变化模式下的权限控制。

对用户而言,一旦登录之后,自己的 Profile 就已经固定,而资源需求要求的Profile则不一定固定,属于变量,最终计算结果近似于查找最短路径,达到用户组这个级别的额外的变化模式(包括继承、包括派生、包括限制、包括组合等),最终 Zero权限框架中合计支持64种Profile配置,如此就解决了资源 能不能 访问的问题。

3.7.1.3.N维安全视图(View)

前文解决了资源 能不能 访问的问题,本章就在可访问的基础上解决 访问多少 的问题,Zero权限框架中的 S_RESOURCES_ACTION 是强绑定关系,它们之间只会单纯对比操作级别和资源需求级别是否可访问,一旦访问成功,就会延生读写操作的边界,在Zero权限框架中读写边界的划定取决于安全视图 S_VIEW 中的定义。

安全视图基础

安全视图的基础维度如下:

字段名 含义 取值

NAME

视图名称

默认取值 DEFAULT。

POSITION

视图位置

默认取值 DEFAULT。

OWNER_TYPE

视图所属

只包含两种:ROLE-角色视图,USER-用户视图。

OWNER

视图所属者ID

如果是角色视图则是角色ID,如果是用户视图则是用户ID。

RESOURCE_ID

视图所属资源

当前视图关联的 S_RESOURCE 资源ID,一个资源可能存在多个安全视图。

POSITION 和 NAME 构造的视图的核心维度,在系统出现不同需求时会起重要作用:

场景 NAME POSITION

单模块无视图管理

DEFAULT

DEFAULT

单模块带视图

?

DEFAULT

多模块无视图管理

DEFAULT

?

多模块多视图管理

?

?

此处解释一下模块的概念,此处的 模块 并非我们开发过程中的模块,此处的模块底层关联模型只有一个,而模块更多是从列表作为入口。例如:

  • 单纯的CRUD应用(角色管理),可能不带模块概念,管理过程中它的模块只有一个,从角色列表进入,然后管理。

  • 带分类的CRUD应用(订单管理),可能带有不同列表管理处理,如正在执行的订单模块、已经完成的订单模块,而此时后台关联的模型依旧是 订单,这种情况下 POSITION 就显得很重要。

而且 POSITION 会比资源多一个维度,通常资源是后接口绑定,如 /api/xxx/search 的资源接口,但这个资源接口由于支持查询引擎语法,可能应用于不同的菜单入口(上述提到的正在执行的订单、已完成的订单)等,这种情况下两个菜单共享了一个资源,而为了针对不同的菜单定义 角色视图/用户视图,最好的方式就是启用 POSITION 参数。如此计算下来,POSITION既不和查询条件绑定(不同页面、不同位置、同一查询条件),也不可以和页面绑定,如果出现 TAB 页签会造成同一个页面中出现两种不同的查询(可能是两种不同的 POSITION),最终它只能和列表的配置绑定,直接在前端中提供它的配置来完成和列表绑定的过程,这一块的用法属于 Zero权限框架中的难点,其应用范围十分广泛,现阶段通常使用场景如下:

  • 按类型划分位置信息:分类字段管理 /ambient/tabular/:type 不同页面取不同的 POSITION,实现抽象态的列表管理,这种思路同样适用于:档案、合同、项目、员工、客户、分类等。

  • 按类型划分位置信息:如待办列表和已办列表,最终访问资源可能都是 /api/todo/search,而由于状态不同,所以设置不同的 POSITION 实现视图的定制。

  • 按流程划分QBE:目前系统中流程右上角的QBE列表页是基于此种逻辑,几乎不使用开发的模式就定制完成。

安全视图类

Zero中存在一个特殊的参数对象:

    public class Vis extends JsonObject

    // 该参数对象使用时可如下:
    @POST
    @Path("/{actor}/search")
    @Address(Addr.Post.SEARCH)
    @Adjust(Orders.MODULE)
    JsonObject search(@PathParam("actor") String actor,
                      @BodyParam JsonObject data,
                      @QueryParam(KName.MODULE) String module,
                      @PointParam(KName.VIEW) Vis view);

该参数的格式比较特殊,通常使用的是 [view,position] 的数据格式,也是此处的 @PointParam 注解解析的内容,它可以将上述格式直接解析成视图的两个核心维度( NAME, POSITION ),并将该维度应用于任意支持它的接口。

窗口定义

安全视图的窗口定义主要依靠下边几个字段:

字段名 含义

PROJECTION

JsonArray格式,执行该视图的列过滤,直接过滤掉接口返回数据的列信息。

CRITERIA

JsonObject格式,执行该视图默认的 Qr 语法注入,启用查询引擎追加接口的查询条件语法。

ROWS

JsonObject格式,针对行数据执行筛选,生成 IN 语句筛选特定行,通常是查询引擎无法做细粒度筛选时的一种折中选择。

VISITANT

布尔值,是否启用 虚拟视图(资源访问者)

  • PROJECTION 会作用于不同类型的前端组件,通常用于 LIST/FORM 两种,Zero框架中的保存列表的列信息以及表单中针对部分表单执行字段过滤就依赖它来完成,它是后置过滤(实际会从数据库中查询出所有信息进行值提取,现阶段没有明显的性能问题)。

  • CRITERIA 主要针对于查询,它会隐式修改查询引擎的 Qr 语法,导致前端发送查询条件在安全视图作用下被直接修改,如果用户中出现了多个角色、多个用户组,则按照最终资源需求中定义的 Profile 来完成查询条件的拼合,默认模式下多个角色之间使用 OR 连接符。

  • ROWS 针对特殊资源提取,提供基于主键的直接命中条件,解决异构查询模式下用户无法使用Join的情况,由于表单是单条数据记录,所以一般表单接口无法支持该属性(设置了也没有效果);通常此属性作用于列表:

    • DATA:在数据层面,列表处理过程中直接针对条件执行过滤,典型应用为:菜单筛选、字典筛选、分类树筛选。

    • META:元数据层面,处理过程中只读取ROWS中设置过的的信息(特殊模式下载界面呈现模式出现ReadOnly时,它的定义位于UI配置中,而不是安全视图层。

虚拟视图(资源访问者)部分参考下一个章节的详细说明,上述限制中,虽然 ROWS 无法直接作用于表单属性级的过滤窗口操作,但是它可以实现动态表单(/api/ui/form)模式下的属性集过滤,比如读取表单的属性有25个,使用 `ROWS`直接过滤掉一部分留下一部分,最终输出的是配置、配置、配置,最终表单引擎根据配置渲染表单,可以完成间接作用流程。

视图检索流程

看完了上述安全视图的方方面面之后,视图检索流程就变得异常简单了,后端会根据访问资源键值生成 session-<METHOD>:<URI>:<POSITION>/<VIEW> 格式的视图缓存键,下边是用户访问某个资源接口时的详细流程:

  1. 用户发送请求到某个资源接口如:/api/xxx/resource

  2. 系统检索该资源是否存在用户级的 S_VIEW 记录(OWNER_TYPE = USER, OWNER = <USER_ID>),如果存在该记录,则直接提取安全视图记录对资源执行前后(BEFORE/AFTER)计算。

  3. 若不存在用户级的 S_VIEW 记录,则继续检索是否有角色级的 S_VIEW 记录,若存在则计算。

  4. 上述两步都不存在时,忽略安全视图,可访问所有内容。

从上述流程可以知道,用户级安全视图优先级比角色高,一般用户级安全视图都是个人视图模式存在,比如某个模块的视图管理,而角色级的视图都是管理员预设,单个用户不可以更改,比如管理员直接针对财务人员以外的角色设置不可访问某些资源的固定列如薪资、账期等。

最终 访问多少 的问题就直接被安全视图处理掉了,不同角色不同用户在此框架之下访问同一个接口时返回数据就可能出现不同,那么这样就解决了资源重用并且 访问多少 的问题。

3.7.1.4.资源访问者(Visitor)
3.7.1.5.可扩展管理端(Region)

3.7.2.管理端

3.7.3.消费端